#ifndef PCI_H_
#define PCI_H_

#include <types.h>

#define PCI_CONFIG_ADDR 0xCF8
#define PCI_CONFIG_DATA 0xCFC
#define PCI_CONFIG_ENABLE (1 << 31)

#define PCI_SPACE_DEVICEID 0x00
#define PCI_SPACE_STATUS 0x04
#define PCI_SPACE_CLASSREV 0x08
#define PCI_SPACE_TYPE 0x0C
#define PCI_SPACE_BAR0 0x10
#define PCI_SPACE_BAR1 0x14
#define PCI_SPACE_BAR2 0x18
#define PCI_SPACE_BAR3 0x1C
#define PCI_SPACE_BAR4 0x20
#define PCI_SPACE_BAR5 0x24
#define PCI_SPACE_CARDBUS 0x28
#define PCI_SPACE_SUBSYS 0x2C
#define PCI_SPACE_EXPROM 0x30
#define PCI_SPACE_INT 0x3C

#define PCI_BIOS32_START 0xE0000
#define PCI_BIOS32_END 0xFFFFF
#define PCI_BIOS32_SIGNATURE "_32_"
#define PCI_BIOS_SERVICE_ID 0x49435024
#define PCI_IF_PCI_FUNC_ID 0xB100
#define PCI_IF_BIOS_PRESENT 0x0001
#define PCI_IF_SIGNATURE "PCI "

#define ERR_PCI_BIOS32_NOTFOUND 1
#define ERR_PCI_BIOS32_NOTIMPL 2
#define ERR_PCI_BIOS_NOTIMPL 3
#define ERR_PCI_BAD_MECH 4
#define ERR_PCI_DEV_INVALID 5

typedef struct pci_bios32_sd
{
	char signature[4];
	dword entry_ptr;
	byte revision;
	byte length;
	byte checksum;
	byte reserved[5];
} __attribute__((packed)) pci_bios32_sd_t;

typedef struct pci_bios_interface
{
	byte mechanism;
	byte version_major;
	byte version_minor;
	byte last_bus;
} __attribute__((packed)) pci_bios_interface_t;

typedef struct bios32_indirect
{
	dword addr;
	word segment;
} __attribute__((packed)) bios32_indirect_t;

typedef struct pci_config
{
	word device_id;
	word vendor_id;
	dword class_code;
	byte revision;
	word subsys_id;
	word subsys_vendor_id;
	byte int_pin;
	byte int_line;
} __attribute__((packed)) pci_config_t;

pci_bios32_sd_t * find_pci_bios32(void *start, void *end);
int pci_scan_bus(dword bus);
int pci_scan_device(dword bus, dword dev, dword func);
dword pci_read_dword(dword bus, dword dev, dword func, dword reg);
void pci_write_dword(dword bus, dword dev, dword func, dword reg, dword val);
int init_pci(void);

typedef struct pci_device_table
{
	word id;
	char *string;
} pci_device_table_t;

typedef struct pci_vendor_table
{
	word id;
	char *string;
	pci_device_table_t *devices;
} pci_lookup_table_t;

#endif /*PCI_H_*/
